# coding=utf-8
import json
import math
import random
import csv
import ctypes
import time
from sesson2.subject1.session2_1_main import timelimited
from tkinter.constants import PROJECTING
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import configparser


from .import view_map as vm
from .import base_fun as fun
from .import networkx_graph as xgh
import sesson1.globalvar  as Globalvar
import sesson1.udp  as udp
import sesson1.run as sesson1
from threading import Thread
import threading
import inspect
import ctypes

# from math import *

def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")

def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)

def timelimited(exectime=None):
    def decorator(function):
        def decorator2(*args, **kwargs):
            time_out = exectime if not exectime is None else kwargs.pop("exectime",None)
            if time_out is None:
                return Exception("exec time param missing ")
            class TimeLimited(threading.Thread):
                def __init__(self, _error=None, ):
                    Thread.__init__(self)
                    self._error = _error
                    self._result = None
                def run(self):
                    try:
                        result = function(*args, **kwargs)
                        if result is None:
                            self._result = True
                        else:
                            self._result = result
                    except Exception as err:
                        self._error = Exception(err)
            t = TimeLimited()
            t.setDaemon(True)
            t.start()
            if not time_out is False:
                t.join(time_out)
                if isinstance(t._error, Exception):
                    return t._error
                else:
                    if t._result is None:
                        stop_thread(t)
                        return Exception("Time out!")
                    else:
                        return t._result
        return decorator2
    return decorator
# import view_map as vm
# import base_fun as fun
# import networkx_graph as xgh

colors = list(dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS).keys())


def filter_people():
    with open("./data/data2cpp.json", 'r') as load_f:
        load_dict = json.load(load_f)

    rescue_peoples = [[], [], []]

    for idex in range(len(load_dict["car_points"])):
        for point in load_dict["car_points"][idex]["pass_points"]:
            rescue_peoples[idex].append(
                [point["longitude"], point["latitude"]])
    # print(rescue_peoples)
    datasets = (np.array(rescue_peoples[0]), np.array(
        rescue_peoples[1]), np.array(rescue_peoples[2]))

    for idex in range(3):
        # plt.figure() #另起一张新的画布
        plt.plot(datasets[idex][:, 1], datasets[idex][:, 0],
                 color=colors[idex], label='t2', marker='o')

    # plt.show()
    return datasets


def GetDistance(p1, p2):
    lat1 = p1[1]
    long1 = p1[0]
    lat2 = p2[1]
    long2 = p2[0]
    return GetDistance2(lat1, long1, lat2, long2)


def GetDistance2(lat1, long1, lat2, long2):
    # print(lat1, long1, lat2, long2)
    val = math.sin(lat1 * 0.01745329) * math.sin(lat2 * 0.01745329) + math.cos(lat1 *
                                                                               0.01745329) * math.cos(lat2 * 0.01745329) * math.cos((long1 - long2) * 0.01745329)
    return 6378137 * math.acos(val)


def cal_cost(lstt, point):
    minval = []
    minlist = []
    for j in range(20):
        sum = 0
        ls = list(lstt)
        for i in range(len(ls)-1):
            sum += GetDistance(point[ls[i]], point[ls[i+1]])
        # print(sum)
        minval.append(sum)
        minlist.append(ls)
        random.shuffle(lstt)
    ret_ls = minlist[minval.index(min(minval))]
    return min(minval), minval.index(min(minval)), ret_ls


def cal_cost(lstt, point):
    minval = []
    minlist = []
    for j in range(20):
        sum = 0
        ls = list(lstt)
        for i in range(len(ls)-1):
            sum += GetDistance(point[ls[i]], point[ls[i+1]])
        # print(sum)
        minval.append(sum)
        minlist.append(ls)
        random.shuffle(lstt)
    ret_ls = minlist[minval.index(min(minval))]
    return min(minval), minval.index(min(minval)), ret_ls


def calpath(txdatx):

    dlen = len(txdatx)
    minindex = txdatx[0]  # min(txdatx)
    new_list = []
    new_list.append(minindex)  # listname[index]
    # print(new_list, txdatx.index(min(txdatx)))
    del txdatx[0]  # txdatx.index(min(txdatx))
    # print(min(txdatx))
    dis = []
    while(len(txdatx) > 0):
        for idd in range(len(txdatx)):
            dis.append(GetDistance2(
                txdatx[idd][0], txdatx[idd][1], minindex[0], minindex[1]))
        minindex = txdatx[dis.index(min(dis))]
        new_list.append(minindex)
        del txdatx[dis.index(min(dis))]
        dis = []
    data2 = pd.DataFrame(new_list, columns=[
                         'longitude', 'latitude', 'label', 'people_id'])
    return data2


def msort(txdatx):
    newlis = list(txdatx)
    ret = calpath(newlis)
    # print(txdatx)
    return ret

@timelimited(exectime=90)# 设置运行超时时间S，优先使用，exectime=False是不加定时
def sesson2_2_main(round_id):
    rp, center = fun.out_data(round_id)  # 计算待救人ID

    d1 = rp[0]  # .sample(n=75)
    d2 = rp[1]  # .sample(n=75)
    d3 = rp[2]  # .sample(n=75)

    sd1 = np.array(d1).tolist()  # np.ndarray()
    sd2 = np.array(d2).tolist()  # np.ndarray()
    sd3 = np.array(d3).tolist()  # np.ndarray()
    sd0 = (msort(sd1), msort(sd2), msort(sd3))
    # fun.toJons(addd, "./data/data2cpp.json")
    # fun.toJonsPeolpes(sd0)
    # gen_people_path()
    # points = fun.view_maps(True)
    # filter_people()  # 过滤一些
    # read_txt()
    # plt.show()
    xgh._main_fun(sd0, round_id)

    return True


def read_txt():
    fname = 'outpath.csv'
    points = fun.view_maps(False)
    paths = [[], [], []]
    point_ids = [[], [], []]
    with open(fname, 'r+', encoding='utf-8') as txtf:
        lines = txtf.readlines()
        # print(len(lines))

    for index, line in enumerate(lines):
        point_ids[index] = (list(int(i) for i in line.split(",")))

    for index in range(3):
        #         # print(line)

        for val in point_ids[1]:
            paths[index].append(points[val])
    data1 = pd.DataFrame(paths[0], columns=['x', 'y'])
    data2 = pd.DataFrame(paths[1], columns=['x', 'y'])
    data3 = pd.DataFrame(paths[2], columns=['x', 'y'])
    fun.toJons((data1, data2, data3), './data/send_data.json')


def view_my_path(paths):
    for idex, path in enumerate(paths):
        data = np.array(path)
        plt.plot(data[:, 1], data[:, 0],
                 color=colors[idex+3], label='t2'+str(idex), marker='.')
    # plt.show()


def view_randm_path():
    # gen_people_path()
    points = fun.view_maps(True)
    path = []
    paths = []
    point_id = []
    point_ids = []
    fname = 'C:\\Users\\tang\\Desktop\\RHZT\R1\\jsontest3\\outrandmpath.csv'
    with open(fname, 'r+', encoding='utf-8') as txtf:
        lines = txtf.readlines()
    for index, line in enumerate(lines):
        point_ids.append(list(int(i) for i in line.split(",")))
    for index, val in enumerate(point_ids):
        for val in val:
            path.append(points[val])
        paths.append(path)
        path = []
    # print(paths)

    view_my_path(paths)

    filter_people()


def gen_people_path():
    # gen_path = "C:\\Users\\tang\\Desktop\\cmd\\cpp_app\\data\\gen_people_25.csv"
    gen_path = "C:\\Users\\tang\\Desktop\\cmd\\data\\gen_people_25.csv"
    # test_dll = ctypes.cdll.LoadLibrary(
    #     'C:\\Users\\tang\\Desktop\\cmd\\cpp_app\\x64\\Release\\jsontest.dll')
    # ret = test_dll.gen_people_25(1000)
    # print(ret)
    gen_paths(
        reapath=gen_path,
        out_path='C:\\Users\\tang\\Desktop\\RHZT\\Test_Exam\\session\\session_2\\subject_2\\team_path_2.json'
    )


def gen_different_path():
    gen_path = "C:\\Users\\tang\\Desktop\\cmd\\cpp_app\\data\\gen_people_25.csv"
    test_dll = ctypes.cdll.LoadLibrary(
        'C:\\Users\\tang\\Desktop\\cmd\\cpp_app\\x64\\Release\\jsontest.dll')
    ret = test_dll.gen_differnent_paths(10, 15000, 10)
    print(ret)
    gen_paths(
        reapath=gen_path,
        out_path='C:\\Users\\tang\\Desktop\\RHZT\\Test_Exam\\session\\session_2\\subject_2\\team_path_02.json'
    )


def gen_paths(reapath, out_path):
    points = fun.view_maps(False)
    paths = [[], [], []]
    point_ids = [[], [], []]
    with open(reapath, 'r+', encoding='utf-8') as txtf:
        lines = txtf.readlines()
        # print(len(lines))

    for index, line in enumerate(lines):
        point_ids[index] = (list(int(i) for i in line.split(",")))

    for index in range(3):
        for val in point_ids[index]:
            paths[index].append(points[val])

    data1 = pd.DataFrame(paths[0], columns=['y', 'x'])
    data2 = pd.DataFrame(paths[1], columns=['y', 'x'])
    data3 = pd.DataFrame(paths[2], columns=['y', 'x'])
    # print(out_path)
    fun.toJons((data1, data2, data3), out_path)


# Ui界面调用进行预处理


def preprocess(Objdll, s1, ipconfigPath):
    # 读图
    cf = configparser.ConfigParser()
    cf.read(ipconfigPath+"/ipconfig.ini")
    local_ip = cf.get("entrant", "ip")
    local_port = cf.get("entrant", "port")
    s1.bind((local_ip, int(local_port)))  # 绑定ip和端口
    # 设置发送（客户端）的ip和端口
    client_ip = cf.get("client", "ip")
    client_port = cf.get("client", "port")
    dest_dir = (client_ip, int(client_port))
    Globalvar.set_send_id_addr(dest_dir)
    # local_ip = udp.socket.gethostbyname(udp.socket.gethostname())
    # s1.bind((local_ip, 10014))
    # print("local_ip:",local_ip)
    print('Bind UDP on 10014...')
    # 创建线程接收数据
    recv_thread = udp.threading.Thread(target=udp.recvmsg, args=(s1, Objdll))
    recv_thread.start()
    return True
# Ui界面调用运行科目1


def session2_2_main(Objdll, s):
    exam_round = 1
    # 判断每场考试是否结束，结束为true，否则为false，初始为true方便第一次发送
    finish = True
    while True:
        if finish:
            # 发送准备指令
            sesson1.send_ready(2, 2, exam_round, s)
            finish = False
        if Globalvar.get_recv_msg_content():
            Globalvar.set_recv_msg_content(False)
            # 根据报文读取对应科目、情景下的client_path_.json文件
            temp_content = Globalvar.get_msg_content_value()
            examRound = Globalvar.get_exam_round()
            try:
                time_start = time.time()
                sesson2_2_main(examRound)  # 计算结果
            except Exception as e:
                print(e)
                ret = -1
                p1 = -1
                print('Timeout Error Catched!')

            # 数据传入完成，算法计算完毕，向平台发送完成指令
            udp.sentmsg(temp_content[0], temp_content[1], s, Globalvar.get_send_id_addr(
            ), exam_round=examRound, info=3)
            finish = True
            # 一场考试结束后将考试场次+1
            exam_round += 1
            print("运行成功，准备下次考试")


if __name__ == '__main__':
    sesson2_2_main(2)
    vm.show_gen_22_path()
    
